//
//  SLOrderFile.m
//  SLKit
//
//  Created by S_LINE on 2021/7/29.
//  Copyright © 2021 S_LINE. All rights reserved.
//

#import "SLOrderFile.h"

#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
#import <dlfcn.h>
#import <libkern/OSAtomic.h>

//原子队列，其目的是保证写入安全，线程安全
static  OSQueueHead symbolList = OS_ATOMIC_QUEUE_INIT;
//定义符号结构体，以链表的形式
typedef struct {
    void *syNode;
    void *nextNode;
}SLSyNode;

@implementation SLOrderFile

static BOOL stop = NO;

/*
 - start：起始位置
 - stop：并不是最后一个符号的地址，而是整个符号表的最后一个地址，最后一个符号的地址=stop-4（因为是从高地址往低地址读取的，且stop是一个无符号int类型，占4个字节）。stop存储的值是符号的
 */
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                                         uint32_t *stop) {
    static uint32_t N;
    if (start == stop || *start) return;
    for (uint32_t *x = start; x < stop; x++) {
        *x = ++N;
    }
}

/*
 可以全面hook方法、函数、以及block调用，用于捕捉符号，是在多线程进行的，这个方法中只存储pc，以链表的形式
 
 - guard 是一个哨兵，告诉我们是第几个被调用的
 */
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
    if (stop) return;
    //获取PC
    /*
     - PC 当前函数返回上一个调用的地址
     - 0 当前这个函数地址，即当前函数的返回地址
     - 1 当前函数调用者的地址，即上一个函数的返回地址
     */
    void *syNode = __builtin_return_address(0);
    //创建node，并赋值
    SLSyNode *node = malloc(sizeof(SLSyNode));
    *node = (SLSyNode){syNode, NULL};
    
    //加入队列
    //符号的访问不是通过下标访问，是通过链表的next指针，所以需要借用offsetof（结构体类型，下一个的地址即next）
    OSAtomicEnqueue(&symbolList, node, offsetof(SLSyNode, nextNode));
}

+ (void)outputOrderFile
{
    NSMutableArray *symbolArray = [[NSMutableArray alloc] init];
    
    while (YES)
    {
        SLSyNode * node = OSAtomicDequeue(&symbolList, offsetof(SLSyNode, nextNode));
        
        if (node == NULL)
        {
            break;
        }
        
        Dl_info info = {0};
        dladdr(node->syNode, &info);
        NSString *symbol = @(info.dli_sname);
        
        BOOL isObjc = [symbol hasPrefix:@"+["] || [symbol hasPrefix:@"-["];
        NSString *realSymbol = isObjc ? symbol : [@"_" stringByAppendingString:symbol];
        if (![symbolArray containsObject:realSymbol])
        {
            [symbolArray addObject:realSymbol];
        }
        
        NSArray *timelineSymbolArray = [[symbolArray reverseObjectEnumerator] allObjects];
        NSString *orderString = [timelineSymbolArray componentsJoinedByString:@"\n"];
        
        NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"card.order"];
        [[NSFileManager defaultManager] createFileAtPath:filePath contents:[orderString dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
    }
}

@end
